package com.cps116.friendstracker;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;

import com.cps116.resource.ReverseGeocode;
import android.app.Service;
import android.content.Intent;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;

public class UserLocationUpdateService extends Service implements LocationListener{

	public static final int MIN_DIST_CHANGE = 0;
	public static final String TAG = UserLocationUpdateService.class.getSimpleName();
	private long userUpdateTimePreference;	//default 0?
	public static final long DEFAULT_MAX_UPDATE_TIME = Long.MAX_VALUE;
	private static final long DEFAULT_NO_UPDATE_TIME = 0;
	//	private UserLocationUpdater updater;	--ignore
	private boolean runFlag = false;

	//Current user location
	private LocationManager locationManager;
	private Geocoder geocoder;

	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}


	@Override
	public void onCreate() {
		super.onCreate();
		//set default update time
		userUpdateTimePreference = 0;	

		//set up LocationManager and LocationListner
		locationManager = (LocationManager) getSystemService(LOCATION_SERVICE); // 
		geocoder = new Geocoder(this, Locale.ENGLISH);
	}


	@Override
	public void onStart(Intent intent, int startId) {
		super.onStart(intent, startId);

		//for 1.6
		Log.d("UserLocationUpdateService", "UserLocationUpdateService start'd");

		//get the parts...
		runFlag = intent.getBooleanExtra("run_flag", false);
		userUpdateTimePreference = intent.getLongExtra("delay_interval", DEFAULT_MAX_UPDATE_TIME);

		if(userUpdateTimePreference == DEFAULT_NO_UPDATE_TIME)
			//this means that the user is choosing not to update...we call it "deferred update"
			userUpdateTimePreference = DEFAULT_MAX_UPDATE_TIME;

		//register the location listening devices provider
		locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, userUpdateTimePreference, MIN_DIST_CHANGE, this);


		//start the thread updater thing											--ignore
		//		updater = new UserLocationUpdater(userUpdateTimePreference);		--ignore
		//		updater.start();													--ignore

	}

	//THIS IS  SUPPORTED ONLY IN >1.6!!
	//	@Override
	//	public int onStartCommand(Intent intent, int flags, int startId) {
	//		super.onStartCommand(intent, flags, startId);
	//
	//		Log.d("UserLocationUpdateService", "UserLocationUpdateService start'd");
	//
	//		//get the parts...
	//		runFlag = intent.getBooleanExtra("run_flag", false);
	//		userUpdateTimePreference = intent.getLongExtra("delay_interval", DEFAULT_MAX_UPDATE_TIME);
	//
	//		if(userUpdateTimePreference == DEFAULT_NO_UPDATE_TIME)
	//			//this means that the user is choosing not to update...we call it "deferred update"
	//			userUpdateTimePreference = DEFAULT_MAX_UPDATE_TIME;
	//
	//		//register the location listening devices provider
	//		locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, userUpdateTimePreference, MIN_DIST_CHANGE, this);
	//
	//
	//		//start the thread updater thing											--ignore
	//		//		updater = new UserLocationUpdater(userUpdateTimePreference);		--ignore
	//		//		updater.start();													--ignore
	//
	//		return START_STICKY;
	//	}




	public void setServiceRunFlag(boolean runflag)
	{
		//setting up the run flag
		runFlag = runflag;
	}


	public boolean getServiceRunFlag()
	{
		//checking if service is runnable
		return runFlag;
	}

	public long getServiceUpdateInterval()
	{
		return userUpdateTimePreference;
	}


	@Override
	public void onDestroy() {
		super.onDestroy();	

		Log.d("UserLocationUpdateService" ,"stopped");

		//stop the locationManger
		locationManager.removeUpdates(this);

		//stop the running service
		setServiceRunFlag(false);


		//stop thread service...
		//		updater.interrupt();		--ignore
		//		updater = null;				--ignore
	}


	/**
	 * Here's where we specify our action when time has elapsed
	 */
	private final int FIRST_ELEMENT = 0;

	private void performLocationUpdate(Location location) {

		Location lastLocation = locationManager
				.getLastKnownLocation(LocationManager.GPS_PROVIDER); // 
		if (lastLocation != null) {


			try {
				//this does not work in AVD 2.2/2.3 etc...
				//				List<Address> addresses = geocoder.getFromLocation(loc_lat, loc_long, 1);
				List<Address> addresses = geocoder.getFromLocation(37.42233, -122.083, 1);
				Log.d("UserLocationUpdateService", "" + addresses.size());

				if (!addresses.isEmpty()){
					//process the address
					//TODO: fill this when the android 2.2 is corrected!	
				}

			} catch (IOException e) {
				//this is a temporary hack that works!
				Log.d(TAG, "Geocoder doesn't work under emulation.");
				String[] possibleAddresses = ReverseGeocode.getFromLocation(location.getLatitude(), location.getLongitude(), 3);

				if (possibleAddresses != null)
				{//testing the sizes of the hack...
					Log.d("UserLocationUpdateService", "addresses size: " + possibleAddresses.length);

					if(possibleAddresses.length > 0)
					{
						//process locations and the address of the first returned element
						processAddress(possibleAddresses[FIRST_ELEMENT], location);
					}
				}

			}
		}
	}




	private void processAddress(String nearest_address, Location location) {
		//you have the address and the location...GL
		//Google maps itself does not have all the locations defined...Sometimes your address will just be an empty string
		Log.d("UserLocationUpdateService", "street address: " + nearest_address);

		//broadcast the user location list on the profile activity &along with appropriate time stamp
		//test if you are using the empty string...if so, then 
		Intent newLocationUpdateIntent = new Intent("com.cps116.NEW_LOCATION_SIGNAL");

		if(nearest_address.trim().equals("")){
			//in case that google map's location is uncharted
			newLocationUpdateIntent.putExtra("new-address", "GOOGLE MAP LOCATION UNCHARTED!");
		}
		else{
			newLocationUpdateIntent.putExtra("new-address", nearest_address);
		}

		//formatting UTC time... into zone specific time
		long UTC_time = location.getTime();
		String sign = UTC_time > 0 ? "+" : "-";
		UTC_time = Math.abs(UTC_time);      
		int hours = (int) (UTC_time/3600);
		int mins = (int) ((UTC_time%3600)/60);
		String zone = String.format("GMT%s%02d:%02d", sign, hours, mins);          
		TimeZone t = TimeZone.getTimeZone(zone);

		SimpleDateFormat simpleForm = new SimpleDateFormat();
		simpleForm.setTimeZone(t);

		Log.d("UserLocationUpdateService", "time UTC: " + ""+ simpleForm.format(new Date()));

		//do need toconver UTC time to local time and just save the local time onto the database
		newLocationUpdateIntent.putExtra("new-time", simpleForm.format(new Date()));

		//sending broadcast
		this.sendBroadcast(newLocationUpdateIntent);

		//register with the server database (latitude, longitude, timestamp) && need to get the user's unique id from facebook user...		
		//need to do an HTTP post request here!!

		//call a thread dispatch to performing post requests to the database...
		String user_id = ((FriendsTrackerApplication) this.getApplication()).getFacebookUserId();
		//map setup: 

		//setting up all the objects for tread transmission
		Map<String, String> post_data = new HashMap<String, String>();

		Log.d("UserLocationUpdateService", user_id);

		post_data.put("user_id", user_id);
		post_data.put("time_simple", simpleForm.format(new Date()));
		post_data.put("road", newLocationUpdateIntent.getStringExtra("new-address"));
		post_data.put("longitude", "" + location.getLongitude());
		post_data.put("latitude", "" + location.getLatitude());
		
		//running the thread for updating the server...
		//not working...not working
		new UserLocationServerPost().execute(post_data);
		
//		HttpClient httpclient = new DefaultHttpClient();
//		HttpPost httppost = new HttpPost("http://dbcourse.cs.duke.edu/tnd6/FFQueries/pushlocation.php");
//
//		try {
//			// Add your data
//			List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(5);
//			nameValuePairs.add(new BasicNameValuePair("userid", user_id));
//			nameValuePairs.add(new BasicNameValuePair("time", simpleForm.format(new Date())));
//			nameValuePairs.add(new BasicNameValuePair("longitude", "" + location.getLongitude()));
//			nameValuePairs.add(new BasicNameValuePair("latitude", "" + location.getLatitude()));
//			nameValuePairs.add(new BasicNameValuePair("road", "road" ));
//			
//			httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
//
//			// Execute HTTP Post Request
//			HttpResponse response = httpclient.execute(httppost);
//
//
//		} catch (ClientProtocolException e) {
//		} catch (IOException e) {
//		}

		
	}



	public void onLocationChanged(Location location) {
		//this is called when the location changes
		performLocationUpdate(location);
	}


	public void onProviderDisabled(String provider) {
	}


	public void onProviderEnabled(String provider) {
	}


	public void onStatusChanged(String provider, int status, Bundle extras) {
	}


	/**
	 * Create asynctask class to feed information to do HTTP post request
	 */
	private class UserLocationServerPost extends AsyncTask<Map<String, String>, String, String>{


		@Override
		protected String doInBackground(Map<String, String>... maps) {

			//HTTPPOST (userid, time, longitude, latitude, road)
			
			Log.d("UserLocationServerPost", "server update is going...");

			Map<String, String>data_map = maps[0];

			String user_id = (String) data_map.get("user_id");
			//TODO: change the status of the time
			String update_time = (String) data_map.get("time_simple");
			String longitude = (String) data_map.get("longitude");
			String latitude = (String) data_map.get("latitude");
			
			String road = (String) data_map.get("road");
			
			Log.d("UserLocationServerPost", longitude + " " + latitude);
			
			
			HttpClient httpclient = new DefaultHttpClient();
			HttpPost httppost = new HttpPost("http://dbcourse.cs.duke.edu/tnd6/FFQueries/pushlocation.php");

			try {
				// Add your data
				List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(5);
				nameValuePairs.add(new BasicNameValuePair("userid", user_id));
				nameValuePairs.add(new BasicNameValuePair("time", new Date().toString()));
				nameValuePairs.add(new BasicNameValuePair("longitude", longitude));
				nameValuePairs.add(new BasicNameValuePair("latitude", latitude));
				nameValuePairs.add(new BasicNameValuePair("road", road));
				
				httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));

				// Execute HTTP Post Request
				HttpResponse response = httpclient.execute(httppost);
				
				return "service succeeded";

			} catch (ClientProtocolException e) {
				return "service failed";
			} catch (IOException e) {
				return "service failed";
			}


		}


		@Override
		protected void onPostExecute(String result) {
			super.onPostExecute(result);

			Log.d("UserLocationUpdateService", result);

		}
	}




	//	/**
	//	 * IGNORE!!! WORKS PERFECTLY, BUT WILL NOT BE USED
	//	 *
	//	 */
	//	private class UserLocationUpdater extends Thread {
	//
	//		//check if this is set up before the constructor call
	//		private long delay = Long.MAX_VALUE;
	//		private UserLocationUpdateService locationServiceCaller;
	//
	//		public UserLocationUpdater(long delay)
	//		{
	//			super("user-location-updater-thread");
	//			this.delay = delay;
	//		}
	//
	//
	//		@Override
	//		public void run() {
	//			//set the service Caller here
	//			locationServiceCaller = UserLocationUpdateService.this;
	//
	//			Log.d("testing", "update service started");
	//
	//			while(locationServiceCaller.getServiceRunFlag())
	//			{
	//				try{
	//					//you want to make sure the first you do is sleep for the delayed time
	//					Thread.sleep(delay);
	//					Log.d("UserLocationUpdateService", "thread is running");
	//
	//					//perform all the location updates stuff
	//					locationServiceCaller.performLocationUpdate();
	//
	//				} 
	//				catch (InterruptedException e)
	//				{
	//					//end the thread...
	//					locationServiceCaller.setServiceRunFlag(false);
	//				}
	//			}
	//
	//		}
	//		
	//		@Override
	//		public synchronized void start() {
	//			super.start();
	//		}
	//
	//		@Override
	//		public void destroy() {
	//			super.destroy();
	//			Log.d("UserLocationUpdateService", "thread update service terminated");
	//		}
	//	}


}
